﻿#include "OiPCH.hpp"

namespace Oi
{
    namespace PLScannerCore
    {
        class ModbusCommTaskImpl : public Impl<ModbusCommTask>
        {
        public:
            void init();
            void exit();

        public:
            bool _quit;

            QMutex _mutex;
            QList<QVector<ushort>> _write;
            QList<int> _writePos;

            ushort _read[10] = {0};

        public:
            QSharedPointer<Util::Modbus> _device;
        };

        void ModbusCommTaskImpl::init()
        {
            OI_Q(ModbusCommTask);

            _quit = false;

//             _action = -1;
//             _data = nullptr;

            if (!PLScanner::instance()->simulation)
            {
                _device = Util::ModbusService::instance()->get(PLScanner::instance()->modbusTcp_Name.toStdString().c_str());

                if(_device.isNull())
                {
                    _device = Util::Modbus::create(PLScanner::instance()->modbusTcp_Addr.toStdString().c_str(), PLScanner::instance()->modbusTcp_Port.toInt());

                    Util::ModbusService::instance()->add(PLScanner::instance()->modbusTcp_Addr.toStdString().c_str(), _device);

                    OiWarning() << "Create Success, Modbus:" << PLScanner::instance()->modbusTcp_Addr << " Port:" << PLScanner::instance()->modbusTcp_Port;
                }

                q->start();
            }
        }


        void ModbusCommTaskImpl::exit()
        {
            OI_Q(ModbusCommTask);
            
            q->exit();
        }

        ModbusCommTask::ModbusCommTask()
        {
            OI_I(ModbusCommTask)->init();
        }

        ModbusCommTask::~ModbusCommTask()
        {
            OI_E(ModbusCommTask)->exit();
        }

        void ModbusCommTask::exit()
        {
            OI_F(ModbusCommTask);
            if (isRunning())
            {
                quit();
                wait();
            }

            if (d->_device)
            {
                d->_device.clear();
            }

            Util::ModbusService::instance()->exit();
        }

        void ModbusCommTask::run()
        {
            OI_F(ModbusCommTask);

            bool quit = d->_quit;
            int action = 0;
            int addr = 0;
            int nb = 10;
            ushort buffer[10];
            QVector<ushort> writeBuffer;
            int writeAddr = 0;

            while(!quit)
            {
                if(!d->_device->isValid())
                {
                    PLScannerError::instance()->reportError(PLScannerError::ERR_MODBUS_INVALID);
                }

                else
                {
                    PLScannerError::instance()->clearError(PLScannerError::ERR_MODBUS_INVALID);

                    if(action == 0)
                    {
                        if(d->_device->readRegisters(addr, nb, buffer) >= 0)
                        {
                            d->_mutex.lock();
                            memcpy(d->_read, buffer, sizeof(ushort) * nb);
                            d->_mutex.unlock();

                            if (PLScanner::instance()->debugging)
                            {
                                //OiWarning() << "Modbus Read:" << buffer[0] << ";" << buffer[1] << ";" << buffer[2] << ";" << buffer[3];
                            }
                        }
                        else
                        {
                            if (PLScanner::instance()->debugging)
                            {
                                OiWarning() << "Modbus Read failed";
                            }
                        }
                    }
                    else if(action > 0)
                    {
                        if(d->_device->writeRegisters(writeAddr, writeBuffer.size(), writeBuffer.data()) >= 0)
                        {
                           
                        }
                    }
                }

                d->_mutex.lock();

                quit = d->_quit;
                action = d->_write.size();
                if (action > 0)
                {
                    writeBuffer = d->_write[0];
                    d->_write.pop_front();

                    writeAddr = d->_writePos[0];
                    d->_writePos.pop_front();
                }

                d->_mutex.unlock();

                

                //OiWarning() << "Read Dir:" << (d->_read[1] & 16);

                QThread::msleep(10);
            }
        }

        void ModbusCommTask::quit()
        {
            OI_F(ModbusCommTask);
            QMutexLocker lock(&d->_mutex);
            d->_quit = true;
        }

        void ModbusCommTask::write(int addr, int nb, ushort *data)
        {
            OI_F(ModbusCommTask);

            if (PLScanner::instance()->simulation)
            {
                return;
            }

            if (!isRunning())
            {
                return;
            }

            QVector<ushort> buffer;
            for (int i = 0; i < nb; i ++)
            {
                buffer.push_back(data[i]);
            }

            QMutexLocker lock(&d->_mutex);
            d->_writePos.append(addr);
            d->_write.append(buffer);
          
        }

        void ModbusCommTask::read(int addr, int nb, ushort* data)
        {
            OI_F(ModbusCommTask);

            if(PLScanner::instance()->simulation)
            {
                return;
            }

            if (!isRunning())
            {
                return;
            }

            QMutexLocker lock(&d->_mutex);
            for (int i = 0; i < nb; i++)
            {
                data[i] = d->_read[i + addr];
            }
        }

        ModbusCommTask* ModbusCommTask::instance()
        {
            static ModbusCommTask task;
            return &task;
        }

    }
}